//@author A0091838U
//This class support the addTask functionality of CrossOff, which support flexible adding.
package crossoff.logic;

import java.io.IOException;

import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;

import crossoff.datetime.DateTimeHandler;
import crossoff.db.History;
import crossoff.db.HistoryType;
import crossoff.db.Storage;
import crossoff.model.GUIobject;
import crossoff.model.LFobject;
import crossoff.model.Pair;
import crossoff.model.Task;

public class CommandAddTask {

	private static LFobject Lf = null;
	private static boolean flag_DateTime_start = false;
	private static boolean flag_DateTime_end = false;
	private static boolean flag_Date = false;
	private static boolean flag_Time_Only = false;
	private static boolean flag_Time_Only_end = false;
	private static boolean flag_Date_Only = false;
	private static String description = null;
	private static LocalTime startTime = null;
	private static LocalTime endTime = null;
	private static LocalDate startDate = null;
	private static LocalDate endDate = null;
	private static Pair<DateTime, Integer> newPair = null;
	private static Pair<LocalTime, Integer> PairTime = null;
	private static Pair<LocalDate, Integer> PairDate = null;
	private static int split_start = 0;
	private static int split_end = 0;
	private static String[] commandSplit = null;

	public static GUIobject addTask(String command, boolean flag)
			throws IOException {
		commandSplit = command.split("\\s+");
		initilize();

		// check if the String given contains DateTime Object
		// return the Remaining String respect to DateTime String retrieve out
		// Follow the step by step, if Don't have DateTime Object, then check
		// LocalDate Object then LocalTime Object. Update them respectively to
		// do the Live Feedback.

		command = extractDateTimeFirst(command, commandSplit);
		if (flag_DateTime_start == true) {
			commandSplit = command.split("\\s+");
			newPair = null;
			command = extractDateTimeSecond(command, commandSplit);
			command = updateDateTime(command, commandSplit);
		}
		if (flag_DateTime_start == false) {
			command = extractDateSecond(command, commandSplit);
			command = updateDateTime(command);
			command = updateDateTimeCurrect(command);
		}
		updateDescription(command);
		updateToValidateDateTime();
		createLFobject();

		return createGUIobject(flag);
	}

	private static GUIobject createGUIobject(boolean flag) throws IOException {
		if (flag == false)
			return new GUIobject(Lf);
		else {
			return createGUIobject();
		}
	}

	private static GUIobject createGUIobject() throws IOException {
		if (startDate != null && startTime != null) {
			if (endDate == null && endTime == null) {
				Task savedTask = Storage.getInstance().saveAtTimed(description,
						startTime, startDate);
				History.getInstance().addToUndo(savedTask, HistoryType.ADDED);
				GUIobject guiobject = new GUIobject("TASK ADDED", "");
				guiobject.setNewTask(savedTask.getTaskID());
				return guiobject;
			} else if (endDate != null && endTime != null) {
				if (endDate != null && endDate.isBefore(startDate)) {
					return new GUIobject("INVALID INPUT", "");
				} else if (endDate != null && endTime != null
						&& endDate.equals(startDate)
						&& endTime.isBefore(startTime)) {
					return new GUIobject("INVALID INPUT", "");
				} else {
					Task savedTask = Storage.getInstance()
							.saveFromTimedMutipleDay(description, startTime,
									endTime, startDate, endDate);
					History.getInstance().addToUndo(savedTask,
							HistoryType.ADDED);
					GUIobject guiobject = new GUIobject("TASK ADDED", "");
					guiobject.setNewTask(savedTask.getTaskID());
					return guiobject;
				}
			}
		} else if (endDate != null && endTime != null) {
			Task savedTask = Storage.getInstance().saveDeadline(description,
					endTime, endDate);
			History.getInstance().addToUndo(savedTask, HistoryType.ADDED);
			GUIobject guiobject = new GUIobject("TASK ADDED", "");
			guiobject.setNewTask(savedTask.getTaskID());
			return guiobject;
		} else {
			Task savedTask = Storage.getInstance().saveFloating(description);
			History.getInstance().addToUndo(savedTask, HistoryType.ADDED);
			GUIobject guiobject = new GUIobject("TASK ADDED", "");
			guiobject.setNewTask(savedTask.getTaskID());
			return guiobject;
		}
		return new GUIobject("INVALID INPUT", "");
	}

	private static void createLFobject() {
		Lf = new LFobject();
		Lf.setDescription(description);
		Lf.setEndDate(endDate);
		Lf.setStartDate(startDate);
		Lf.setEndTime(endTime);
		Lf.setStartTime(startTime);
	}

	private static void updateToValidateDateTime() {
		if (endTime != null && startTime != null && endTime.isBefore(startTime)) {
			checkValidateDateTime();
		} else if (endTime != null && startTime != null) {
			checkValidateDate();
		}
	}

	private static void checkValidateDate() {
		if (endDate != null && startDate != null && endDate.isBefore(startDate)) {
			endDate = startDate;
		}
	}

	private static void checkValidateDateTime() {
		if (endDate != null && startDate != null
				&& (!endDate.isAfter(startDate))) {
			endDate = startDate.plusDays(1);
		}
	}

	private static String updateDateTimeCurrect(String command) {
		if (flag_Date_Only == false) {
			PairTime = null;
			command = extractTimeSecond(command, commandSplit);
			command = updateTimeSecond(command);
		}
		return command;
	}

	private static String updateTimeSecond(String command) {
		if (flag_Time_Only == true) {
			commandSplit = command.split("\\s+");
			PairTime = null;
			command = extractTimeThird(command, commandSplit);
			updateToCurrent();
		}
		return command;
	}

	private static void updateToCurrent() {
		if (flag_Time_Only_end == true) {
			updateDateTimeCurrent();
		} else {
			updateDateTimeCurrentSecond();
		}
	}

	private static String updateDateTime(String command) {
		if (flag_Date_Only == true) {
			commandSplit = command.split("\\s+");
			PairTime = null;
			command = extractTimeSecond(command, commandSplit);
			command = extractTimeSecond(command);
		}
		return command;
	}

	private static String extractTimeSecond(String command) {
		if (flag_Time_Only == true) {
			commandSplit = command.split("\\s+");
			PairTime = null;
			command = extractTimeSecondEnd(command, commandSplit);
			updateDateTime();
		} else {
			updateDateTimeSecond();
		}
		return command;
	}

	private static void updateDateTime() {
		if (flag_Time_Only_end == false) {
			updateDateTimeFirst();
		}
	}

	private static String updateDateTime(String command, String[] commandSplit) {
		if (flag_DateTime_end == false) {
			command = extractDateFirst(command, commandSplit);
			command = extractDateFristString(command, commandSplit);
		}
		return command;
	}

	private static String extractDateFristString(String command,
			String[] commandSplit) {
		if (flag_Date == false) {
			command = extractTimeFirst(command, commandSplit);
		}
		return command;
	}

	private static void updateDescription(String command) {
		if (Lf != null)
			description = Lf.getDescription();
		else
			description = command;
	}

	private static void updateDateTimeCurrentSecond() {
		if (startTime != null)
			startDate = DateTimeHandler.getInstance().figureOutCurrentDate(
					startTime);
		else
			endDate = DateTimeHandler.getInstance().figureOutCurrentDate(
					endTime);
	}

	private static void updateDateTimeCurrent() {
		startDate = DateTimeHandler.getInstance().figureOutCurrentDate(
				startTime);
		endDate = startDate;
	}

	private static String extractTimeThird(String command, String[] commandSplit) {
		for (int i = 0; i <= commandSplit.length - 1; i++) {
			PairTime = DateTimeHandler.getInstance().evaluateTimeStr(
					commandSplit, i);
			if (PairTime.getFirst() != null) {
				split_start = i;
				split_end = PairTime.getSecond() + i;
				Lf = evaluate_Time(split_start, split_end, commandSplit,
						PairTime);
				command = Lf.getDescription();
				if (startTime == null) {
					startTime = Lf.getStartTime();
					if (startTime == null)
						startTime = Lf.getEndTime();
				} else {
					endTime = Lf.getEndTime();
					if (endTime == null)
						endTime = Lf.getStartTime();
				}
				flag_Time_Only_end = true;
				break;
			}
		}
		return command;
	}

	private static void updateDateTimeSecond() {
		if (endDate != null) {
			endTime = new LocalTime(23, 59);
		} else {
			endDate = startDate;
			startTime = new LocalTime(0, 0);
			endTime = new LocalTime(23, 59);
		}
	}

	private static void updateDateTimeFirst() {
		if (endDate != null) {
			if (endTime == null) {
				endTime = startTime;
				startTime = null;
			}
		} else {
			if (endTime != null) {
				endDate = startDate;
				startDate = null;
			}
		}
	}

	private static String extractTimeSecondEnd(String command,
			String[] commandSplit) {
		for (int i = 0; i <= commandSplit.length - 1; i++) {
			PairTime = DateTimeHandler.getInstance().evaluateTimeStr(
					commandSplit, i);
			if (PairTime.getFirst() != null) {
				command = extractPairTimeSecond(commandSplit, i);
				break;
			}
		}
		return command;
	}

	private static String extractPairTimeSecond(String[] commandSplit, int i) {
		String command;
		split_start = i;
		split_end = PairTime.getSecond() + i;
		Lf = evaluate_Time(split_start, split_end, commandSplit, PairTime);
		command = Lf.getDescription();
		if (startTime == null) {
			startTime = Lf.getStartTime();
			if (startTime == null)
				startTime = Lf.getEndTime();
		} else {
			endTime = Lf.getEndTime();
			if (endTime == null)
				endTime = Lf.getStartTime();
		}
		if (startDate != null)
			endDate = startDate;
		else
			startDate = endDate;
		flag_Time_Only_end = true;
		return command;
	}

	private static String extractTimeSecond(String command,
			String[] commandSplit) {
		for (int i = 0; i <= commandSplit.length - 1; i++) {
			PairTime = DateTimeHandler.getInstance().evaluateTimeStr(
					commandSplit, i);
			if (PairTime.getFirst() != null) {
				command = extractPairTimeFirst(commandSplit, i);
				break;
			}
		}
		return command;
	}

	private static String extractPairTimeFirst(String[] commandSplit, int i) {
		String command;
		split_start = i;
		split_end = PairTime.getSecond() + i;
		Lf = evaluate_Time(split_start, split_end, commandSplit, PairTime);
		command = Lf.getDescription();
		startTime = Lf.getStartTime();
		endTime = Lf.getEndTime();
		flag_Time_Only = true;
		return command;
	}

	private static String extractDateSecond(String command,
			String[] commandSplit) {
		for (int i = 0; i <= commandSplit.length - 1; i++) {
			PairDate = DateTimeHandler.getInstance().evaluateDateStr(
					commandSplit, i);
			if (PairDate.getFirst() != null) {
				command = extactPairDateFirst(command, commandSplit, i);
				break;
			}
		}
		return command;
	}

	private static String extactPairDateFirst(String command,
			String[] commandSplit, int i) {
		split_start = i;
		split_end = PairDate.getSecond() + i;
		Lf = evaluate_Date(split_start, split_end, commandSplit, PairDate);
		description = command;
		command = Lf.getDescription();
		startDate = Lf.getStartDate();
		endDate = Lf.getEndDate();
		flag_Date_Only = true;
		return command;
	}

	private static String extractTimeFirst(String command, String[] commandSplit) {
		for (int i = 0; i <= commandSplit.length; i++) {
			PairTime = DateTimeHandler.getInstance().evaluateTimeStr(
					commandSplit, i);
			if (PairTime.getFirst() != null) {
				command = extractPairTime(commandSplit, i);
				break;
			}

		}
		return command;
	}

	private static String extractPairTime(String[] commandSplit, int i) {
		String command;
		split_start = i;
		split_end = PairTime.getSecond() + i;
		Lf = evaluate_Time(split_start, split_end, commandSplit, PairTime);
		command = Lf.getDescription();
		if (startTime == null) {
			startDate = endDate;
			startTime = Lf.getStartTime();
			if (startTime == null)
				startTime = Lf.getEndTime();
		}
		if (endTime == null) {
			endDate = startDate;
			endTime = Lf.getEndTime();
			if (endTime == null)
				endTime = Lf.getStartTime();
		}
		flag_Date = true;
		return command;
	}

	private static String extractDateFirst(String command, String[] commandSplit) {
		for (int i = 0; i <= commandSplit.length - 1; i++) {
			PairDate = DateTimeHandler.getInstance().evaluateDateStr(
					commandSplit, i);
			if (PairDate.getFirst() != null) {
				command = extractPairDate(commandSplit, i);
				break;
			}
		}
		return command;
	}

	private static String extractPairDate(String[] commandSplit, int i) {
		String command;
		split_start = i;
		split_end = PairDate.getSecond() + i;
		Lf = evaluate_Date(split_start, split_end, commandSplit, PairDate);
		command = Lf.getDescription();
		if (startDate == null) {
			startDate = Lf.getStartDate();
			if (startDate == null)
				startDate = Lf.getEndDate();
			startTime = new LocalTime(0, 0);
		}
		if (endDate == null) {
			endDate = Lf.getEndDate();
			if (endDate == null)
				endDate = Lf.getStartDate();
			endTime = new LocalTime(23, 59);
		}
		flag_Date = true;
		return command;
	}

	private static String extractDateTimeSecond(String command,
			String[] commandSplit) {
		for (int i = 0; i <= commandSplit.length - 1; i++) {
			newPair = DateTimeHandler.getInstance().evaluateDateTimeStr(
					commandSplit, i);
			if (newPair != null) {
				command = extractNewPair(commandSplit, i);
				break;
			}
		}
		return command;
	}

	private static String extractNewPair(String[] commandSplit, int i) {
		String command;
		split_start = i;
		split_end = newPair.getSecond() + i;
		Lf = evaluate_DateTime(split_start, split_end, commandSplit, newPair);
		command = Lf.getDescription();
		if (startTime == null)
			startTime = Lf.getStartTime();
		if (startDate == null)
			startDate = Lf.getStartDate();
		if (endTime == null)
			endTime = Lf.getEndTime();
		if (endDate == null)
			endDate = Lf.getEndDate();
		flag_DateTime_end = true;
		return command;
	}

	private static String extractDateTimeFirst(String command,
			String[] commandSplit) {
		for (int i = 0; i <= commandSplit.length - 1; i++) {
			newPair = DateTimeHandler.getInstance().evaluateDateTimeStr(
					commandSplit, i);
			if (newPair != null) {
				command = extractNewPairSecond(commandSplit, i);
				break;
			}
		}
		return command;
	}

	private static String extractNewPairSecond(String[] commandSplit, int i) {
		String command;
		split_start = i;
		split_end = newPair.getSecond() + i;
		Lf = evaluate_DateTime(split_start, split_end, commandSplit, newPair);
		flag_DateTime_start = true;
		command = Lf.getDescription();
		startTime = Lf.getStartTime();
		endTime = Lf.getEndTime();
		startDate = Lf.getStartDate();
		endDate = Lf.getEndDate();
		return command;
	}

	private static void initilize() {
		Lf = null;
		flag_DateTime_start = false;
		flag_DateTime_end = false;
		flag_Date = false;
		flag_Time_Only = false;
		flag_Time_Only_end = false;
		flag_Date_Only = false;
		description = null;
		startTime = null;
		endTime = null;
		startDate = null;
		endDate = null;
		newPair = null;
		PairTime = null;
		PairDate = null;
		split_start = 0;
		split_end = 0;

	}

	private static LFobject evaluate_DateTime(int split_start, int split_end,
			String[] commandSplit, Pair<DateTime, Integer> newPair) {
		String description = null;
		LocalDate startDate = null, endDate = null;
		LocalTime startTime = null, endTime = null;
		DateTime startPair = null, endPair = null;
		if (split_start == 0) {
			startPair = newPair.getFirst();
			startDate = startPair.toLocalDate();
			startTime = startPair.toLocalTime();
		}
		if (split_start != 0) {
			if (commandSplit[split_start - 1].equalsIgnoreCase("from")) {
				startPair = newPair.getFirst();
				startDate = startPair.toLocalDate();
				startTime = startPair.toLocalTime();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("at")) {
				startPair = newPair.getFirst();
				startDate = startPair.toLocalDate();
				startTime = startPair.toLocalTime();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("on")) {
				startPair = newPair.getFirst();
				startDate = startPair.toLocalDate();
				startTime = startPair.toLocalTime();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("by")) {
				endPair = newPair.getFirst();
				endDate = endPair.toLocalDate();
				endTime = endPair.toLocalTime();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("to")) {
				endPair = newPair.getFirst();
				endDate = endPair.toLocalDate();
				endTime = endPair.toLocalTime();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("-")) {
				endPair = newPair.getFirst();
				endDate = endPair.toLocalDate();
				endTime = endPair.toLocalTime();
				split_start--;
			} else {
				startPair = newPair.getFirst();
				startDate = startPair.toLocalDate();
				startTime = startPair.toLocalTime();
			}
		}
		String remaining = "";

		String remaining1 = "";
		StringBuilder builder = new StringBuilder();
		StringBuilder builder1 = new StringBuilder();
		for (int j = 0; j < split_start; j++) {
			builder.append(commandSplit[j]);
			if (j != split_start - 1) {
				builder.append(" ");
			}
		}
		remaining = builder.toString();
		for (int j = split_end; j < commandSplit.length; j++) {
			builder1.append(commandSplit[j]);
			if (j != commandSplit.length - 1) {
				builder1.append(" ");
			}
		}
		remaining1 = builder1.toString();
		description = connenct(remaining, remaining1);

		return new LFobject(description, startDate, endDate, startTime, endTime);
	}

	private static LFobject evaluate_Date(int split_start, int split_end,
			String[] commandSplit, Pair<LocalDate, Integer> PairDate) {
		String description = null;
		LocalDate startDate = null, endDate = null;
		LocalTime startTime = null, endTime = null;
		if (split_start == 0) {

			startDate = PairDate.getFirst();
		}
		if (split_start != 0) {
			if (commandSplit[split_start - 1].equalsIgnoreCase("from")) {
				startDate = PairDate.getFirst();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("at")) {
				startDate = PairDate.getFirst();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("on")) {
				startDate = PairDate.getFirst();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("by")) {
				endDate = PairDate.getFirst();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("to")) {
				endDate = PairDate.getFirst();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("-")) {
				endDate = PairDate.getFirst();
				split_start--;
			} else {
				startDate = PairDate.getFirst();
			}
		}
		String remaining = "";
		String remaining1 = "";
		StringBuilder builder = new StringBuilder();
		StringBuilder builder1 = new StringBuilder();
		for (int j = 0; j < split_start; j++) {
			builder.append(commandSplit[j]);
			if (j != split_start - 1) {
				builder.append(" ");
			}
		}
		remaining = builder.toString();
		for (int j = split_end; j < commandSplit.length; j++) {
			builder1.append(commandSplit[j]);
			if (j != commandSplit.length - 1) {
				builder1.append(" ");
			}
		}
		remaining1 = builder1.toString();
		description = connenct(remaining, remaining1);

		return new LFobject(description, startDate, endDate, startTime, endTime);
	}

	private static LFobject evaluate_Time(int split_start, int split_end,
			String[] commandSplit, Pair<LocalTime, Integer> PairTime) {
		String description = null;
		LocalDate startDate = null, endDate = null;
		LocalTime startTime = null, endTime = null;
		if (split_start == 0) {

			startTime = PairTime.getFirst();
		}
		if (split_start != 0) {
			if (commandSplit[split_start - 1].equalsIgnoreCase("from")) {
				startTime = PairTime.getFirst();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("at")) {
				startTime = PairTime.getFirst();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("on")) {
				startTime = PairTime.getFirst();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("by")) {
				endTime = PairTime.getFirst();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("to")) {
				endTime = PairTime.getFirst();
				split_start--;
			} else if (commandSplit[split_start - 1].equalsIgnoreCase("-")) {
				endTime = PairTime.getFirst();
				split_start--;
			} else {
				startTime = PairTime.getFirst();
			}
		}
		String remaining = "";
		String remaining1 = "";
		StringBuilder builder = new StringBuilder();
		StringBuilder builder1 = new StringBuilder();
		for (int j = 0; j < split_start; j++) {
			builder.append(commandSplit[j]);
			if (j != split_start - 1) {
				builder.append(" ");
			}
		}
		remaining = builder.toString();
		for (int j = split_end; j < commandSplit.length; j++) {
			builder1.append(commandSplit[j]);
			if (j != commandSplit.length - 1) {
				builder1.append(" ");
			}
		}
		remaining1 = builder1.toString();
		description = connenct(remaining, remaining1);

		return new LFobject(description, startDate, endDate, startTime, endTime);
	}

	private static String connenct(String description, String new_description) {
		if (description.equals(""))
			return new_description;
		else if (new_description.equals(""))
			return description;
		else
			return description + " " + new_description;

	}

	public static GUIobject addFloating(String command) throws IOException {
		Task savedTask = Storage.getInstance().saveFloating(command);
		History.getInstance().addToUndo(savedTask, HistoryType.ADDED);
		GUIobject guiobject = new GUIobject("TASK ADDED", "");
		guiobject.setNewTask(savedTask.getTaskID());
		return guiobject;
	}
}
